﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VRacer.Packets;
using System.Windows;
using VRacer.Connection;

namespace VRacer
{
    /// <summary>
    /// Odpowiada za symulowanie gry na serwerze.
    /// Rozpoznaje otrzymane pakiety w metodzie <c>Receive</c> i przekazuje dalej obsluge do odpowiedniej metody postaci On[TPacketName]Packet(ServerPlayer player, TPacketName login).
    /// Po nawiązaniu polączenia wysyła pakiet typu WelcomePacket, po ktorym oczekuje pakietu LoginPacket po otrzymaniu ktorego inni gracze otrzymuja informacje o nowym zawodniku na serwerze.
    /// Nastepnie nasluchuje informacji czy wszyscy gracze sa gotowi do gry (pakiet GameStartPacket) i gdy to nastapi zaczyna gre tym samym pakietem.
    /// W tym miejscu gra czeka na pakiety OnPlayerMoving ktore opisuja zamierzony ruch gracza. Dokonuje analizy ruchu i jesli ruch jest niemozliwy nic sie nie dzieje lub sa odsylane odpowiednie pakiety kolizji zderzenia czy zwyklego ruchu.
    /// Po kazdym przemieszczeniu gracza jest wysylany pakiet informujacy o nastepnym graczu w kolejnce oraz o tym czy rozpaczeto nowa kolejke.
    /// </summary>
    public sealed class ServerGame : NetGame<ServerConnection, ServerPlayer>, IPacketReceiver
    {

        public ServerGame()
        {
            Connection = new ServerConnection();
            Connection.ConnectionAccepted += new EventHandler<ConnectionAcceptedEventArgs>(serverConnection_ConnectionAccepted);
        }

        void serverConnection_ConnectionAccepted(object sender, ConnectionAcceptedEventArgs e)
        {
            var remotePlayer = new ServerPlayer(e.Client);
            remotePlayer.PacketReceived += remotePlayer_PacketReceived;
            remotePlayer.Disconnected += Connection_Disconnected;

            remotePlayer.Send(new WelcomePacket()
                {
                    TrackID = CurrentTrack.ID
                });

            this.Add(remotePlayer);
        }

        void Connection_Disconnected(object sender, EventArgs e)
        {
            var player = sender as PlayerBase;
            Remove(player);
            Send(new PlayerRemovedPacket(player));

            if (ActivePlayer == player)
                ContinueTurn();
        }

        /// <summary>
        /// Logika zachowania gdy zawodnik ma kolizje
        /// </summary>
        /// <param name="uderzajacy">The uderzajacy.</param>
        /// <param name="uderzony">The uderzony.</param>
        /// <param name="collisionPoint">The collision point.</param>
        /// <returns></returns>
        protected override bool HandlePlayerCollision(PlayerBase uderzajacy, PlayerBase uderzony, Point collisionPoint)
        {
            Send(new PlayerCollisionPacket(uderzajacy, uderzony, collisionPoint));
            return base.HandlePlayerCollision(uderzajacy, uderzony, collisionPoint);
        }

        /// <summary>
        /// Logika zachowania gdy zawodnik ma wypadek
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="crashPoint">The crash point.</param>
        /// <param name="destinationPoint">The destination point.</param>
        /// <returns></returns>
        protected override bool HandlePlayerCrashed(PlayerBase player, Point crashPoint, Point destinationPoint)
        {
            Send(new PlayerCrashedPacket(player, crashPoint, FindPointAfterCollision(crashPoint, player)));
            return base.HandlePlayerCrashed(player, crashPoint, destinationPoint);
        }

        /// <summary>
        /// Logika zachowania gdy zawodnik przejechal przez mete
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="finishPoint">The finish point.</param>
        /// <param name="destinationPoint">The destination point.</param>
        /// <returns></returns>
        protected override bool HandlePlayerCrossedFinish(PlayerBase player, Point finishPoint, Point destinationPoint)
        {
            bool result = base.HandlePlayerCrossedFinish(player, finishPoint, destinationPoint);
            Send(new PlayerLapsPacket(player));
            return result;
        }

        /// <summary>
        /// Logika zachowania gdy zawodnik przejechal przez start
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="finishPoint">The finish point.</param>
        /// <param name="destinationPoint">The destination point.</param>
        /// <returns></returns>
        protected override bool HandlePlayerCrossedStart(PlayerBase player, Point finishPoint, Point destinationPoint)
        {
            bool result = base.HandlePlayerCrossedStart(player, finishPoint, destinationPoint);
            Send(new PlayerLapsPacket(player));
            return result;
        }

        /// <summary>
        /// Logika zachowania gdy gra chce umiescic nowy bonus na mapie
        /// </summary>
        /// <param name="bonus">The bonus.</param>
        /// <returns></returns>
        protected override bool HandleNewBonus(IBonus bonus)
        {
            return true;
        }

        /// <summary>
        /// Called when [player moved].
        /// </summary>
        /// <param name="player">The player.</param>
        protected override void OnPlayerMoved(PlayerBase player)
        {
            Send(new PlayerMovedPacket(player, player.Position));
            base.OnPlayerMoved(player);
        }


        /// <summary>
        /// Remotes the player_ packet received.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="packet">The packet.</param>
        void remotePlayer_PacketReceived(object sender, PacketBase packet)
        {
            if (Dispatcher.CheckAccess())
                Receive((ServerPlayer)sender, packet);
            else
                Dispatcher.Invoke(new PacketReceivedHandler(remotePlayer_PacketReceived), sender, packet);
        }

        /// <summary>
        /// Obsluguje przychodzacy pakiet od danego zawodnika
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="packet">The packet.</param>
        private void Receive(ServerPlayer player, PacketBase packet)
        {
            Console.WriteLine("R (" + player.Title + "): " + packet.ToString());

            if (PacketReceived != null)
                PacketReceived(player, packet);


            switch (packet.Type)
            {
                case PacketType.Login:
                    OnLoginPacket(player, (LoginPacket)packet);
                    break;
                case PacketType.PlayerMoving:
                    OnPlayerMovingPacket(player, (PlayerMovingPacket)packet);
                    break;
                case PacketType.GameState:
                    OnGameStatePacket(player, (GameStatePacket)packet);
                    break;
                case PacketType.Message:
                    Send(packet);
                    break;
            }
        }

        #region Packet handlers

        /// <summary>
        /// Called when [game state packet].
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="gameStart">The game start.</param>
        private void OnGameStatePacket(ServerPlayer player, GameStatePacket gameStart)
        {
            player.Ready = true;
            bool ready = true;

            lock (Players)
            {
                foreach (var serverPlayer in Players)
                    if (serverPlayer is ServerPlayer)
                    {
                        ready &= ((ServerPlayer)serverPlayer).Ready;
                    }
            }

            if (ready)
                Start();
        }

        /// <summary>
        /// Called when [login packet].
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="login">The login.</param>
        private void OnLoginPacket(ServerPlayer player, LoginPacket login)
        {
            player.ID = login.ID;
            player.Title = login.Name;
            player.Version = login.Version;

            foreach (var serverPlayer in Players)
                if (player != serverPlayer && serverPlayer.ID != 0)
                    player.Send(new NewPlayerPacket(serverPlayer));

            PlayersByID.Add(player.ID, player);

            Send(new NewPlayerPacket(player));
        }

        /// <summary>
        /// Called when [player moving packet].
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="playerMoving">The player moving.</param>
        private void OnPlayerMovingPacket(ServerPlayer player, PlayerMovingPacket playerMoving)
        {
            player.Move(playerMoving.NewPosition);
        }

        #endregion

        protected override void LoadTracks()
        {
            Tracks = TracksCollection.Create("Tracks/");
        }

        /// <summary>
        /// Wysyla pakiet do wszystkich podlaczonych klientow
        /// </summary>
        /// <param name="packet">The packet.</param>
        public override void Send(PacketBase packet)
        {
            Console.WriteLine("S: " + packet.ToString());

            foreach (var player in Players)
            {
                if (player is IPacketSender && player.ID != 0)
                {
                    ((IPacketSender)player).Send(packet);
                }
            }
        }

        /// <summary>
        /// Konczy ture bierzacego gracza i rozpoczyna ture nastepnego. W razie potrzeby rozpoczyna kolejna kolejke
        /// </summary>
        public override void ContinueTurn()
        {
            var nextPlayer = GetNextPlayerInTurn();
            var continueTurn = new ContinueTurnPacket();

            while (nextPlayer == null)
            {
                continueTurn.IsNewTurn = true;
                continueTurn.Turns++;
                StartNewTurn();
                nextPlayer = GetNextPlayerInTurn(0);
            }

            continueTurn.NextPlayerID = nextPlayer.ID;
            ContinueTurn(nextPlayer);
            Send(continueTurn);
        }

        /// <summary>
        /// Inicjalizuje gre. Np. laczy sie z serwerem, pobiera dostepne mapy itp itd.
        /// </summary>
        public override void Initialize()
        {
            CurrentTrack = Tracks[new Random((int)DateTime.Now.Ticks).Next(Tracks.Count)];
            Connection.Connect();
            base.Initialize();
        }

        /// <summary>
        /// Ustawia pozycje startowe zawodnikom
        /// </summary>
        protected override void SetPlayersPositions()
        {
            base.SetPlayersPositions();
            foreach (var player in Players)
                Send(new PlayerPositionPacket(player));
        }

        /// <summary>
        /// Sets the state.
        /// </summary>
        /// <value>The state.</value>
        public override GameState State
        {
            set
            {
                base.State = value;
                Send(new GameStatePacket(value));
            }
        }

        /// <summary>
        /// Finishes this instance.
        /// </summary>
        public override void Finish()
        {
            base.Finish();
            Send(new ScoresPacket(this.Scores));
        }

        #region IPacketReceiver Members

        public event PacketReceivedHandler PacketReceived;

        #endregion
    }
}
